/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

 *(C) Copyright 2006 Marvell International Ltd.  
 * All Rights Reserved 
 */

/**
**  FILENAME:       xllp_i2c.c
**
**  PURPOSE:        Xllp i2c file
**
**
******************************************************************************/

#include "xllp_i2c.h"
#ifdef XLLP_MFP_SCL_OFFSET
// only valid if pins are assigned to non-Power I2C
static XLLP_UINT32_T xllp_i2c_mfp_list[] ={ 	XLLP_MFP_SCL_OFFSET,
					 	XLLP_MFP_SDA_OFFSET,
					 	XLLP_MFP_PIN_EOLIST_MARKER};
static XLLP_MFP_ALT_FN_T xllp_i2c_af_list[] = { XLLP_MFP_SCL_AF, 
						XLLP_MFP_SDA_AF};


#endif// def XLLP_MFP_SCL_OFFSET
static XLLP_MFP_DRIVE_STRENGTH_T xllp_i2c_ds_list[] ={	XLLP_MFP_DEFAULT_DS,
							XLLP_MFP_DEFAULT_DS};

static XLLP_MFP_LPM_OUTPUT_T xllp_i2c_lpm_output_list[] = {
							XLLP_MFP_LPMO_PULL_LOW,
							XLLP_MFP_LPMO_PULL_LOW};

#ifdef XLLP_MFP_SCL_OFFSET
// only valid if pins are assigned to non-Power I2C
    
/* 
 * Initialization to use I2C bus
 *
 * PARAMETERS:
 * P_XLLP_I2C_T I2C_regs structure for i2c regs
 * P_XLLP_CLKMGR_T clkaddr - address of clkmanager
 * XLLP_UINT32_T dev_id - Default slave device id for Bulverde
 *
 * RETURNS: XLLP_STATUS_SUCCESS
 *          XLLP_STATUS_WRONG_PARAMETER
 *          XLLP_STATUS_NO_RESOURCES
 * 
 *  Enable I2C Interface Unit - 
 *   
 *      XLLP_ICR_GCD  - Disable General Call (will be master)
 *      XLLP_ICR_UIE    - Enable I2C unit
 *      XLLP_ICR_SCLEA - Enable I2C Clock Generator 
 *      
 */ 
    XLLP_STATUS_T XllpI2cInit(P_XLLP_I2C_T I2C_regs,
			      P_XLLP_VUINT32_T pMfpRegBase,
			      P_XLLP_MFP_RM_DB_ID_T pMfpRmDb, 
				XLLP_UINT32_T dev_id) 
{
	XLLP_STATUS_T re;
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	if (NULL == I2C_regs || NULL == pMfpRegBase || NULL == pMfpRmDb)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif
	I2C_regs->XLLP_ICR = 0;
	re = XllpMfpResourceManager_List(pMfpRmDb, xllp_i2c_mfp_list,
					XLLP_MFP_RM_ID_XLLP_I2C, XLLP_SET);
	
	//XLLP_STATUS_WRONG_PARAMETER
	//XLLP_STATUS_NO_RESOURCES
	if (XLLP_STATUS_SUCCESS != re) {
		return re;
	}
	//XLLP_STATUS_WRONG_PARAMETER
	re = XllpMfpSetAfDs_List(pMfpRegBase, 
				xllp_i2c_mfp_list,
				xllp_i2c_af_list, 
				xllp_i2c_ds_list);
	if (XLLP_STATUS_SUCCESS != re) {
		XllpMfpResourceManager_List(pMfpRmDb, xllp_i2c_mfp_list,
					     XLLP_MFP_RM_ID_XLLP_I2C, XLLP_CLEAR);
		return re;
	}

	re = XllpMfpConfigureLpmOutputLevel_List(pMfpRegBase, xllp_i2c_mfp_list, xllp_i2c_lpm_output_list);
	if (re != XLLP_STATUS_SUCCESS) {
		XllpMfpResourceManager_List(pMfpRmDb, xllp_i2c_mfp_list,
                                             XLLP_MFP_RM_ID_XLLP_I2C, XLLP_CLEAR);
                return re;
	}	
	re =  XllpMfpActivatePullUpDown_List(pMfpRegBase, xllp_i2c_mfp_list, XLLP_OFF);

	if (re != XLLP_STATUS_SUCCESS) {
                XllpMfpResourceManager_List(pMfpRmDb, xllp_i2c_mfp_list,
                                             XLLP_MFP_RM_ID_XLLP_I2C, XLLP_CLEAR);
                return re;
        }
	/* Setup I2C slave address */ 
	I2C_regs->XLLP_ISAR = dev_id;
	I2C_regs->XLLP_ICR = XLLP_ICR_SCLEA;
	I2C_regs->XLLP_ICR |= XLLP_ICR_UIE;
	return (XLLP_STATUS_SUCCESS);
}//end XllpI2cInit


#endif// def XLLP_MFP_SCL_OFFSET
    
/* 
 * Initialization to use PWRI2C bus
 *
 * PARAMETERS:
 *	P_XLLP_I2C_T I2C_regs structure for i2c regs
 *	P_XLLP_CLKMGR_T clkaddr - address of clkmanager
 *      XLLP_UINT32_T dev_id - Default slave device id for Bulverde
 *
 * RETURNS: XLLP_STATUS_WRONG_PARAMETER
 *			XLLP_STATUS_SUCCESS			
 */ 
 XLLP_STATUS_T XllpPI2cInit(P_XLLP_I2C_T I2C_regs, XLLP_UINT32_T dev_id) 
{
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	if (NULL == I2C_regs)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif	
	I2C_regs->XLLP_ICR = 0;
	/* Setup I2C slave address */ 
	I2C_regs->XLLP_ISAR = dev_id;
	I2C_regs->XLLP_ICR = XLLP_ICR_SCLEA;
	I2C_regs->XLLP_ICR |= XLLP_ICR_UIE;
	return (XLLP_STATUS_SUCCESS);
}//end XllpPi2cinit


/* 
 * Wait for Receive empty status
 *
 * RETURNS: XLLP_STATUS_SUCCESS success
 *          XLLP_STATUS_TIME_OUT time out
 */ 
    XLLP_STATUS_T XllpI2cRxFull(P_XLLP_I2C_T I2C_regs, XLLP_OST_T * pOSTRegs,
				XLLP_INT32_T timeout) 
{
	XLLP_UINT32_T temp;
	while (timeout--)
		{
		temp = I2C_regs->XLLP_ISR;
		if ((temp & XLLP_ISR_IRF) == XLLP_ISR_IRF)
			{
			I2C_regs->XLLP_ISR = temp | XLLP_ISR_IRF;
			return XLLP_STATUS_SUCCESS;
			}
		
		    // delay 0.2 ms here
		    XllpOstDelayMicroSeconds(pOSTRegs, 200);
		}
	return XLLP_STATUS_TIME_OUT;
}


/* Wait for transmit empty status
 *
 * RETURNS:
 *	XLLP_STATUS_SUCCESS
 *	XLLP_STATUS_TIME_OUT
 */ 
    XLLP_STATUS_T XllpI2cTxEmpty(P_XLLP_I2C_T I2C_regs, XLLP_OST_T * pOSTRegs,
				 XLLP_INT32_T timeout) 
{
	XLLP_UINT32_T temp;
	while (timeout--)
		{
		temp = I2C_regs->XLLP_ISR;
		if ((temp & XLLP_ISR_ITE) == XLLP_ISR_ITE)
			{
			I2C_regs->XLLP_ISR = temp | XLLP_ISR_ITE;
			if ((temp & XLLP_ISR_ALD) == XLLP_ISR_ALD)
				{
				I2C_regs->XLLP_ISR |= XLLP_ISR_ALD;
				}
			return XLLP_STATUS_SUCCESS;
			}
		XllpOstDelayMicroSeconds(pOSTRegs, 200);
		}
	return XLLP_STATUS_TIME_OUT;
}//end XllpI2cIxEmpty

XLLP_STATUS_T XllpI2CWrite(P_XLLP_I2C_T I2C_regs, XLLP_OST_T * pOSTRegs,
			    XLLP_UINT8_T slaveAddr,
			    const XLLP_UINT8_T * bytesBuf,
			    XLLP_UINT32_T bytesCount, XLLP_BOOL_T bSendStop) 
{
	XLLP_UINT32_T reg;
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	if (NULL == I2C_regs || NULL == pOSTRegs)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif
	I2C_regs->XLLP_IDBR = (slaveAddr << 1) & ~XLLP_IDBR_MODE;
	reg = I2C_regs->XLLP_ICR;
	reg |= (XLLP_ICR_START | XLLP_ICR_TB);
	reg &= ~(XLLP_ICR_STOP | XLLP_ICR_ALDIE);
	I2C_regs->XLLP_ICR = reg;
	if (XllpI2cTxEmpty(I2C_regs, pOSTRegs, 20) == XLLP_STATUS_TIME_OUT)
		{
		return XLLP_STATUS_TIME_OUT;
		}
	
	    // Send all the bytes
	    while (bytesCount--)
        {
            I2C_regs->XLLP_IDBR = (XLLP_UINT32_T) (*bytesBuf++);
            reg = I2C_regs->XLLP_ICR;
            reg &= ~XLLP_ICR_START;
            reg |= (XLLP_ICR_ALDIE | XLLP_ICR_TB);
            if ((bytesCount == 0) && bSendStop)
                reg |= XLLP_ICR_STOP;

            else
                reg &= ~XLLP_ICR_STOP;
            I2C_regs->XLLP_ICR = reg;
            if (XllpI2cTxEmpty(I2C_regs, pOSTRegs, 250) == XLLP_STATUS_TIME_OUT)
            {
                return XLLP_STATUS_TIME_OUT;
            }
        }
	
	    // Clear the STOP bit always
	    I2C_regs->XLLP_ICR &= ~XLLP_ICR_STOP;
	return XLLP_STATUS_SUCCESS;
}//end XllpI2CWrite

XLLP_STATUS_T XllpI2CRead(P_XLLP_I2C_T I2C_regs, XLLP_OST_T * pOSTRegs,
			    XLLP_UINT8_T slaveAddr, XLLP_UINT8_T * bytesBuf,
			    XLLP_UINT32_T bytesCount, XLLP_BOOL_T bSendStop) 
{
	XLLP_UINT32_T reg;
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	    if (NULL == I2C_regs || NULL == pOSTRegs)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif
	I2C_regs->XLLP_IDBR = (slaveAddr << 1) | XLLP_IDBR_MODE;
	reg = I2C_regs->XLLP_ICR;
	reg |= (XLLP_ICR_START | XLLP_ICR_TB);
	reg &= ~(XLLP_ICR_STOP | XLLP_ICR_ALDIE);
	I2C_regs->XLLP_ICR = reg;
	if (XllpI2cTxEmpty(I2C_regs, pOSTRegs, 20) == XLLP_STATUS_TIME_OUT)
		{
		return XLLP_STATUS_TIME_OUT;
		}
	while (bytesCount--)
		{
		reg = I2C_regs->XLLP_ICR;
		reg &= ~XLLP_ICR_START;
		reg |= XLLP_ICR_ALDIE | XLLP_ICR_TB;
		if (bytesCount == 0)
			{
			reg |= XLLP_ICR_ACKNACK;
			if (bSendStop)
				reg |= XLLP_ICR_STOP;
			
			else
				reg &= ~XLLP_ICR_STOP;
		} else
			{
			reg &= ~XLLP_ICR_ACKNACK;
			}
		I2C_regs->XLLP_ICR = reg;
		if (XllpI2cRxFull(I2C_regs, pOSTRegs, 60) == XLLP_STATUS_TIME_OUT)
			{
			return XLLP_STATUS_TIME_OUT;
			}
		reg = I2C_regs->XLLP_IDBR & 0xFF;
		*bytesBuf++ = (XLLP_UINT8_T) reg;
		}
	I2C_regs->XLLP_ICR &= ~(XLLP_ICR_STOP | XLLP_ICR_ACKNACK);
	return XLLP_STATUS_SUCCESS;
}//end XllpI2CRead

XLLP_I2C_SPEED_T XllpGetSpeed(P_XLLP_I2C_T I2C_regs) 
{
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	if (NULL == I2C_regs)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif
	return ((I2C_regs->
		XLLP_ICR & XLLP_ICR_FM) ? XLLP_I2C_FAST_SPEED :
		XLLP_I2C_NORMAL_SPEED);
}//end XllpGetSpeed

XLLP_I2C_SPEED_T XllpSetSpeed(P_XLLP_I2C_T I2C_regs, XLLP_I2C_SPEED_T speed) 
{
	
#ifdef XLLP_DEBUG_PARAM_CHECK
	    if (NULL == I2C_regs)
		return XLLP_STATUS_WRONG_PARAMETER;
	
#endif
	XLLP_UINT32_T tem;
	XLLP_I2C_SPEED_T pre_speed = XllpGetSpeed(I2C_regs);
	switch (speed) {
	case XLLP_I2C_FAST_SPEED:
		I2C_regs->XLLP_ICR |= XLLP_ICR_FM;
	
	//Be sure the configureation takes effect in case of split transaction problem
		tem = I2C_regs->XLLP_ICR;
		return pre_speed;
		break;
	default:
		I2C_regs->XLLP_ICR &= (~XLLP_ICR_FM);
		
	//Be sure the configuration takes effect in case of split transaction problem
		tem = I2C_regs->XLLP_ICR;
		return pre_speed;
	}
}//end XllpSetSpeed
